package com.timemetro.app;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.timemetro.app.stationObject.InfoNextStation;
import com.timemetro.app.stationObject.Station;
import com.timemetro.app.stationObject.TypeStation;

/**
 * Created by localadmin on 21.05.2015.
 * Класс для расчета кратчайшего маршрута/
 */
public class GraphCalculate {
    private static Logger log = Logger.getLogger(GraphCalculate.class.getName());

    private static boolean isSetMatrix;
    // Матрица смежности
    private static float[][] matrixDistanceBetweenNodeBegEndStations;
    private static int lenghtMatrixDistance;

    //private static int indexBeginStation;
    //private static int indexEndStation;

    private static List<Integer> pathIdBranch = new ArrayList<Integer>();
    //private static Station beginStation;
   // private static Station endStation;

    /*Установка матрицы смежности для поиска маршрута (Индексы матрицы смежности будут
    соответствовать индексам структуры хранения узловых станций)*/
    public static void SetMatrix() throws InterruptedException {
         int countNodeStation = ManagerStation.getCountNodeStations();

//        Запоминаем индексы начальной и конечных станций, добавляем пустые станции
        //indexBeginStation = countNodeStation;
        //indexEndStation = countNodeStation + 1;
        //stationsNodeAndBeginAndEnd.add(new Station());
        //stationsNodeAndBeginAndEnd.add(new Station());

//        Размерность массива равна количеству узловых станций плюс начальная и конечная станции
        lenghtMatrixDistance = countNodeStation + 2;
        matrixDistanceBetweenNodeBegEndStations = new float[lenghtMatrixDistance][lenghtMatrixDistance];
            for (int i = 0; i < countNodeStation; i++)
                for (int j = 0; j < countNodeStation; j++)
                {
                    matrixDistanceBetweenNodeBegEndStations[i][j] =
                            ManagerStation.getDistanceBetweenNodeStation(i,j);
//                    Печать матрицы смежности
//                    log.info("[" + i + "] [" + j + "] " + nodeStation.get(i).getName() + " - " + nodeStation.get(j).getName() + ": " + matrixDistanceBetweenNodeBegEndStations[i][j]);
//                    Thread.sleep(10);
                }
        isSetMatrix = true;
        //printMatrix();
    }

    /*
    *Добавление начальной и конечной станций в матрицу смежности */
    public static void startCalculate() throws InterruptedException {
        if (isSetMatrix == false) log.info("Не была установленна матрица смежности. Для её установки необходимо вызвать метод SetMatrix()");
        calculateMatrix();
        calculateGraph();
    }

    /*Расчет кратчайшего маршрута. Алгоритм Дейкстры*/
    private static void  calculateGraph()
    {
        int startNode = ManagerStation.getIndexBeginStation();
        int finishNode = ManagerStation.getIndexEndStation();

        float distance[] = new float[lenghtMatrixDistance];
        int path[] = new int[lenghtMatrixDistance];
        int count, index = 0,i,u,m=startNode+1;

        boolean visited[] = new boolean[lenghtMatrixDistance];
        for (i=0; i<lenghtMatrixDistance; i++)
        {
            distance[i]=50000; visited[i]=false;
            path[i] = -1;
        }
        distance[startNode]=0;
        for (count=0; count<lenghtMatrixDistance-1; count++)
        {
            float min=50000;
            for (i=0; i<lenghtMatrixDistance; i++)
                if (!visited[i] && distance[i]<=min)
                {
                    min=distance[i]; index=i;
                }
            u=index;
            visited[u]=true;
            for (i=0; i<lenghtMatrixDistance; i++)
                if (!visited[i] && matrixDistanceBetweenNodeBegEndStations[u][i] !=0 && distance[u]!=50000 && distance[u]+ matrixDistanceBetweenNodeBegEndStations[u][i]<distance[i]) {
                    distance[i] = (distance[u] + matrixDistanceBetweenNodeBegEndStations[u][i]);
                    path[i] = u;
                }
        }
        List<Integer> stationsIndexPath = new ArrayList<Integer>();
        int currentIndex = finishNode;
        stationsIndexPath.add(finishNode);
        while (true){
            stationsIndexPath.add(path[currentIndex]);
            currentIndex = path[currentIndex];
            if (currentIndex == startNode) break;
        }
        ManagerStation.setActiveSeveralBranch(stationsIndexPath);
    }

    /*Заполняем матрицу смежности для начальной и конечной станций. */
    private static void calculateMatrix() throws InterruptedException {
        int indexBeginStationInMatrix = matrixDistanceBetweenNodeBegEndStations.length - 2;
        int indexEndStationInMatrix = matrixDistanceBetweenNodeBegEndStations.length - 1;

        List<InfoNextStation> infoNextBeginStation = ManagerStation.getNeighborsNodes(ManagerStation.getBeginStation());
        List<InfoNextStation> infoNextEndStation = ManagerStation.getNeighborsNodes(ManagerStation.getEndStation());

        boolean beginStationIsNode = ManagerStation.beginStationIsNode();
        boolean endStationIsNode = ManagerStation.endStationIsNode();
        try {
            for (int i = 0; i < matrixDistanceBetweenNodeBegEndStations.length; i++)
                for (int j = 0; j < matrixDistanceBetweenNodeBegEndStations.length; j++) {

// Заполняем пред последнюю строку матрицы смежности (BEGIN STATION):
                    if (i == indexBeginStationInMatrix && beginStationIsNode == false) {
                        matrixDistanceBetweenNodeBegEndStations[i][j] = calculate(infoNextBeginStation, i, j);
// Заполняем последнюю строку матрицы смежности (END STATION):
                    } else if (i == indexEndStationInMatrix && endStationIsNode == false) {
                        matrixDistanceBetweenNodeBegEndStations[i][j] = calculate(infoNextEndStation, i, j);
// Заполняем пред последний столбец матрицы смежности (BEGIN STATION):
                    } else if (j == indexBeginStationInMatrix && beginStationIsNode == false) {
                        matrixDistanceBetweenNodeBegEndStations[i][j] = calculate(infoNextBeginStation, j, i);
// Заполняем последний столбец матрицы смежности (END STATION):
                    } else if (j == indexEndStationInMatrix && endStationIsNode == false) {
                        matrixDistanceBetweenNodeBegEndStations[i][j] = calculate(infoNextEndStation, j, i);
// Начальная или конечная станция оказались узловыми, следовательно она уже есть, считать эту не надо:
                    } else if (((beginStationIsNode == true) && (i == indexBeginStationInMatrix || j == indexBeginStationInMatrix))||
                            ((endStationIsNode == true) && (i == indexEndStationInMatrix || j == indexEndStationInMatrix))){
                        matrixDistanceBetweenNodeBegEndStations[i][j] = Float.MAX_VALUE;
                    }
                }
        } catch (Exception ex){
            log.log(Level.SEVERE,"Ошибка заполнения начальной и конечной станций у матрицы смежности. calculateMatrix()", ex);
        }
    }

    private static float calculate(List<InfoNextStation> infoNextStations,int indexI,int indexJ){
        for (InfoNextStation infoNextStation : infoNextStations){
            if (infoNextStation.id == ManagerStation.getIdNodeBegEndStation(indexJ)){
                return infoNextStation.length;
            }
        }
        return Float.MAX_VALUE;

    }

    private static void printMatrix() {
        for (int i = 0; i < matrixDistanceBetweenNodeBegEndStations.length - 2; i++)
            for (int j = 0; j < matrixDistanceBetweenNodeBegEndStations.length -2 ; j++){
                log.info("[" + i + "] [" + j + "] " + ManagerStation.getNameNodeBegEndStation(i) + " - " + ManagerStation.getNameNodeBegEndStation(j) + ": " + matrixDistanceBetweenNodeBegEndStations[i][j]);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }

}
